package aliyunOss

import (
	"context"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"time"

	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/util/gconv"
)

// Policy 结构体定义上传策略
type Policy struct {
	Expiration string        `json:"expiration"`
	Conditions []interface{} `json:"conditions"`
}

type Response struct {
	Accessid  string
	Host      string
	Policy    string
	Signature string
	Expire    int64
	Dir       string
}

type OssConfig struct {
	OssId      string
	OssKey     string
	Bucket     string
	Region     string
	ExpireTime int
}

// 获取 GMT 格式的时间字符串
func GmtISO8601(expire int64) string {
	return time.Unix(expire, 0).UTC().Format("2006-01-02T15:04:05Z")
}

// 获取阿里云签名
func GetOssSignature(ctx context.Context, dir string) (*Response, error) {
	aliyunOss, err := g.Cfg().Get(ctx, "aliyunOss")
	if err != nil {
		return nil, err
	}

	var config OssConfig
	if err := gconv.Scan(aliyunOss, &config); err != nil {
		return nil, err
	}

	host := fmt.Sprintf("https://%s.%s.aliyuncs.com", config.Bucket, "oss-"+config.Region)

	// 当前时间和过期时间
	now := time.Now().Unix()
	expire := int64(config.ExpireTime) // 过期时间
	end := now + expire
	expiration := GmtISO8601(end)

	// 条件设置
	conditions := []interface{}{
		[]interface{}{"content-length-range", 0, 1048576000}, // 最大上传大小
		[]interface{}{"starts-with", "$key", dir},            // 必须以 dir 开头
	}

	// 生成策略
	policy := Policy{
		Expiration: expiration,
		Conditions: conditions,
	}
	policyJSON, err := json.Marshal(policy)
	if err != nil {
		return nil, gerror.New("生成策略失败")
	}
	base64Policy := base64.StdEncoding.EncodeToString(policyJSON)

	// 签名生成
	signature := HmacSHA1(base64Policy, config.OssKey)

	// 构造响应
	response := &Response{
		Accessid:  config.OssId,
		Host:      host,
		Policy:    base64Policy,
		Signature: base64.StdEncoding.EncodeToString(signature),
		Expire:    end,
		Dir:       dir,
	}

	return response, nil
}

// 计算 HMAC-SHA1 签名
func HmacSHA1(data, key string) []byte {
	h := hmac.New(sha1.New, []byte(key))
	h.Write([]byte(data))
	return h.Sum(nil)
}

// 阿里云OSS简单上传文件
func UploadFile(ctx context.Context, fileDir string, ossDir string) (string, error) {
	aliyunOss, err := g.Cfg().Get(ctx, "aliyunOss")
	if err != nil {
		return "", err
	}

	var config OssConfig
	if err := gconv.Scan(aliyunOss, &config); err != nil {
		return "", err
	}

	// 加载默认配置并设置凭证提供者和区域
	provider := credentials.NewStaticCredentialsProvider(config.OssId, config.OssKey)
	cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider).WithRegion(config.Region)

	// 创建OSS客户端
	client := oss.NewClient(cfg)

	// 创建上传对象的请求
	putRequest := &oss.PutObjectRequest{
		Bucket:       oss.Ptr(config.Bucket), // 存储空间名称
		Key:          oss.Ptr(ossDir),        // 对象名称
		ContentType:  oss.Ptr("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
		StorageClass: oss.StorageClassStandard, // 指定对象的存储类型为标准存储
		// Acl:          oss.ObjectACLPrivate,     // 指定对象的访问权限为私有访问
		// Metadata: map[string]string{
		// 	"yourMetadataKey1": "yourMetadataValue1", // 设置对象的元数据
		// },
	}

	// 执行上传对象的请求
	_, err = client.PutObjectFromFile(context.TODO(), putRequest, fileDir)
	if err != nil {
		return "", err
	}

	host := fmt.Sprintf("https://%s.%s.aliyuncs.com/", config.Bucket, "oss-"+config.Region)

	return host + ossDir, nil
}

// 阿里云OSS简单下载文件
func DownloadFile(ctx context.Context, ossDir string, fileDir string) error {
	aliyunOss, err := g.Cfg().Get(ctx, "aliyunOss")
	if err != nil {
		return err
	}

	var config OssConfig
	if err := gconv.Scan(aliyunOss, &config); err != nil {
		return err
	}

	// 加载默认配置并设置凭证提供者和区域
	provider := credentials.NewStaticCredentialsProvider(config.OssId, config.OssKey)
	cfg := oss.LoadDefaultConfig().WithCredentialsProvider(provider).WithRegion(config.Region)

	// 创建OSS客户端
	client := oss.NewClient(cfg)

	// 创建获取对象的请求
	request := &oss.GetObjectRequest{
		Bucket: oss.Ptr(config.Bucket), // 存储空间名称
		Key:    oss.Ptr(ossDir),        // 对象名称
	}

	// 执行获取对象的操作并处理结果
	result, err := client.GetObject(context.TODO(), request)
	if err != nil {
		return err
	}
	defer result.Body.Close() // 确保在函数结束时关闭响应体

	// 一次性读取整个文件内容
	data, err := io.ReadAll(result.Body)
	if err != nil {
		return err
	}

	// 将内容写入到文件
	err = os.WriteFile(fileDir, data, 0644)
	if err != nil {
		return err
	}

	return nil
}
